# -*- coding = utf-8 -*-
# @Time : 2023/6/16 9:45
# @Author : 龙王赘婿_彪少
# @File : mycom_copy.py
# @Software: PyCharm

# 功能：16进制的发送与接收，可供copy的demo
# 经过多次打磨，已经很完整，很够用

import serial
import time
import struct
import binascii
import threading


class COM:
    port = "com30"
    baudrate = 115200
    ser = serial.Serial()
    Read_frequency = 20
    Read_period = 1 / Read_frequency

    def com_init(self):
        self.ser.port = self.port
        self.ser.baudrate = self.baudrate
        self.ser.bytesize = 8
        self.ser.stopbits = 1
        self.ser.parity = 'N'
        self.ser.open()
        self.ser.timeout = self.Read_period  # 阻塞函数的超时时间，单位s，read(),readline(),readall()
        if self.ser.isOpen():
            print('*' * 20 + f"  串口：{self.ser.port}，打开成功，波特率：{self.ser.baudrate}  " + '*' * 20)
            return True
        else:
            print('*' * 20 + "  串口打开失败  " + '*' * 20)
            return False

    # 传入bytes并打印显示
    def read_data_analysis(self, raw_data):
        # 接收ASCII码 形如: b'1234567890liuzhibiao' b'\x11"3D'
        # print(f"接收原始数据：len = {len(raw_data)} data = {raw_data}")
        # hex_data = binascii.b2a_hex(raw_data)
        # 16进制序列显示 形如: len = 8 data = b'11223344'
        # print(f"接收:len = {len(hex_data)} data = {hex_data}")
        print(f"recv：len = {len(raw_data)} data =", end=" ")
        for i in range(len(raw_data)):
            # 接收到的原始数据，本质上就是数字，可直接赋值，只是我们显示的时候各不相同 eg: age = raw_data[0]
            print(f"{raw_data[i]:02x}", end=" ")
        print()
        # self.recv_modbus(raw_data)

    @staticmethod
    def recv_modbus(data):
        # 测试接收：01 06 60 40 00 01 57 de
        s = struct.Struct(">2B3H")
        up = s.unpack(data)
        addr = up[2]
        data = up[3]
        crc = up[4]
        print(f"addr = {addr:04x} data = {data:04x} crc = {crc:04x}")

    # 传入hex序列 形如b'\x01\x06`@\x00\x01'
    def send_hex_to_com(self, hex_data):
        self.ser.write(hex_data)
        print(f"send：len = {len(hex_data)} data =", end=" ")
        for i in range(len(hex_data)):
            print(f"{hex_data[i]:02x}", end=" ")
        print()

    # 传入bytes,或只含hex的string并转换格式发送 形如："0123456789abcdef"
    def send_bytes_to_com(self, send_data):
        hex_data = binascii.a2b_hex(send_data)
        # hex_data = bytes.fromhex(send_data)   # 等效
        self.ser.write(hex_data)
        # print(f"发送原始数据：{hex_data}")
        # print(f"发送:len = {len(send_data)} data = {send_data}")
        print(f"send：len = {len(hex_data)} data =", end=" ")
        for i in range(len(hex_data)):
            print(f"{hex_data[i]:02x}", end=" ")
        print()

    # 传入string并转换gbk格式发送
    def send_string_to_com(self, send_data):
        hex_data = send_data.encode('gbk')
        # hex_data = send_data.encode('utf-8')
        self.ser.write(hex_data)
        # print(f"发送原始数据：{hex_data}")
        print(f"send:len = {len(send_data)} data = {send_data}")

    # 例子1 发送ascii字符串
    def eg_01(self):
        send_data = "0123456789liuzhibiao"
        # ASCII 接收 0123456789liuzhibiao
        self.send_string_to_com(send_data)

    # 例子2 发送hex 只需要按顺序写好hex即可
    def eg_02(self):
        send_data = "0123456789abcdef"
        # hex 接收 01 23 45 67 89 AB CD EF
        self.send_bytes_to_com(send_data)

    # 例子3 发送modbus 封包
    def send_modbus(self):
        addr = 0x6040
        data = 0x0001
        buf = [0x01, 0x06, addr, data]  # modbus
        s = struct.Struct(">2B2H")  # 大端模式
        packed_data = s.pack(*buf)
        crc = self.crc16_modbus(packed_data)
        crc_pack = struct.Struct('H').pack(crc)
        packed_data = packed_data + crc_pack
        self.send_hex_to_com(packed_data)

    # crc16 modbus
    @staticmethod
    def crc16_modbus(datalist):
        # from "https://code84.com/461550.html"
        crc = 0xFFFF
        poly = 0xa001
        for data in datalist:
            crc = (data & 0xFF) ^ crc
            for bit in range(8):
                if (crc & 0x1) != 0:
                    crc >>= 1
                    crc ^= poly
                else:
                    crc >>= 1
        return crc

    # crc16 ccitt
    @staticmethod
    def crc16_ccitt(datalist):
        crc = 0x0000
        poly = 0x1021
        for data in datalist:
            crc ^= data << 8
            for bit in range(8):
                if crc & 0x8000:
                    crc = (crc << 1) ^ poly
                else:
                    crc <<= 1
                crc &= 0xFFFF
        return crc

    def read_thread(self):
        # print("read thread is start!")
        while True:
            raw_data = self.ser.readall()
            if len(raw_data) > 0:
                self.read_data_analysis(raw_data)

    def send_thread(self):
        # print("send thread is start!")
        while True:
            key = input("")  # 输入指令
            if key == "a":
                self.eg_01()
            elif key == "b":
                self.eg_02()
            elif key == "c":
                self.send_modbus()

    def __init__(self):
        if self.com_init():
            th_read = threading.Thread(target=self.read_thread)
            th_send = threading.Thread(target=self.send_thread)
            th_read.start()
            time.sleep(0.01)
            th_send.start()
            th_read.join()
            th_send.join()


if __name__ == "__main__":
    c = COM()
